home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
ka9q
/
nhclb120.zoo
/
eagle.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-11
|
24KB
|
805 lines
/*
* Interface driver for the EAGLE board for KA9Q's TCP/IP on an IBM-PC ONLY!
*
* Written by Art Goldman, WA3CVG - (c) Copyright 1987 All Rights Reserved
* Permission for non-commercial use is hereby granted provided this notice
* is retained. For info call: (301) 997-3838.
*
* 10 Jan 88 ng6q - Corrected IDLE comparison in doegstat.
* 6 Apr 88 ng6q - Changed eg_raw to prevent calling egtxint with a
* packet in sndbuf. Initialized sndq and rcvq in
* eg_attach. Added carrier detect check before
* slot time delay in egtxint. Should make major
* changes to egtxint to avoid delay loops while
* masked for receive interrupts.
*/
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "eagle.h"
#include "8530.h"
#include "ax25.h"
#include "trace.h"
#include <time.h>
struct EGTAB eagle[EGMAX]; /* Device table - one entry per card */
void eg0vec(),write_scc(),rts(),waitmsec();
void (*eghandle[])() = { eg0vec }; /* handler interrupt vector table */
struct egchan egchan[2*EGMAX]; /* channel table - 2 entries per card */
int16 egnbr;
/* Master interrupt handler. One interrupt at a time is handled.
* here. Service routines are called from here.
*/
void
egint(dev)
int16 dev;
{
register char st;
register int16 pcbase;
struct egchan *hp;
void egrxint(),egtxint(),egexint();
eagle[dev].ints++;
pcbase = eagle[dev].addr;
/* Read interrupt status register from channel A */
while((st = read_scc(pcbase+CHANA+CTL,R3)) != 0) {
/* Use IFs to process ALL interrupts pending
* because we need to check all interrupt conditions
*/
if (st & CHARxIP) {
/* Channel A Rcv Interrupt Pending */
hp = &egchan[2 * dev];
egrxint(hp);
} else if (st & CHATxIP) {
/* Channel A Transmit Int Pending */
hp = &egchan[2 * dev];
egtxint(hp);
} else if (st & CHAEXT) {
/* Channel A External Status Int */
hp = &egchan[2 * dev];
egexint(hp);
} else if (st & CHBRxIP) {
/* Channel B Rcv Interrupt Pending */
hp = &egchan[(2 * dev)+1];
egrxint(hp);
} else if (st & CHBTxIP) {
/* Channel B Transmit Int Pending */
hp = &egchan[(2 * dev)+1];
egtxint(hp);
} else if (st & CHBEXT) {
/* Channel B External Status Int */
hp = &egchan[(2 * dev)+1];
egexint(hp);
}
/* Reset highest interrupt under service */
write_scc(hp->base+CTL,R0,RES_H_IUS);
} /* End of while loop on int processing */
}
/* Eagle SIO External/Status interrupts
* This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
* Receiver automatically goes to Hunt on an abort.
*
* If the Tx Underrun interrupt hits, change state and
* issue a reset command for it, and return.
*/
static void
egexint(hp)
register struct egchan *hp;
{
char st, i_state;
i_state = disable(); /* disable interrupts */
hp->exints++;
st = read_scc(hp->base+CTL,R0); /* Fetch status */
/* Check for Tx UNDERRUN/EOM - only in Transmit Mode */
if((hp->rstate==0) && (st & TxEOM)) {
/* if in UNDERRUN, go to FLAGOUT state
* see explanation under egtxint()
* CRC & FLAG now going out, so
* wait for Tx BUffer Empty int
*/
/* If we are not in underrun, this is an unexpected
* underrun. EOM bit should be set, so the SCC will
* now send an abort
*/
if(hp->tstate == UNDERRUN)
hp->tstate = FLAGOUT;
/* Tx Buff EMPTY interrupt occurs after CRC is sent */
}
/* Receive Mode only
* This triggers when hunt mode is entered, & since an ABORT
* automatically enters hunt mode, we use that to clean up
* any waiting garbage
*/
if((hp->rstate == ACTIVE) && (st & BRK_ABRT)) {
hp->rcp = hp->rcvbuf->data;
hp->rcvbuf->cnt = 0; /* rewind on DCD transition */
hp->aborts++; /* nbr aborts * 2 */
}
/* reset external status latch */
write_scc(CTL+hp->base,R0,RES_EXT_INT);
restore(i_state);
}
/* EG receive interrupt handler. The first receive buffer is pre-allocated
* in the init routine. Thereafter, it is filled here, queued out, and a
* new one acquired. CRC, OVERRUN and TOOBIG errors merely 'rewind' the
* pointers and reuse the same buffer.
*/
static void
egrxint(hp)
register struct egchan *hp;
{
register int16 base;
char rse, i_state;
i_state = disable(); /* disable interrupts */
hp->rxints++;
base = hp->base;
if ((read_scc(base+CTL,R0)) & Rx_CH_AV) {
/* there is a char to be stored
* read special condition bits before reading the data char
*/
rse = read_scc(hp->base+CTL,R1); /* get status byte from R1 */
if(rse & Rx_OVR) {
/* Rx overrun - toss buffer */
hp->rcp = hp->rcvbuf->data; /* reset buffer pointers */
hp->rcvbuf->cnt = 0;
hp->rstate = RXERROR; /* set error flag */
hp->rovers++; /* count overruns */
} else if(hp->rcvbuf->cnt >= hp->bufsiz) {
/* Too large -- toss buffer */
hp->toobig++;
hp->rcp = hp->rcvbuf->data; /* reset buffer pointers */
hp->rcvbuf->cnt = 0;
hp->rstate = TOOBIG; /* when set, chars are not stored */
}
/* ok, we can store the received character now */
if(hp->rstate == ACTIVE) { /* If no errors... */
*hp->rcp++ = inportb(base+DATA); /* char to rcv buff */
hp->rcvbuf->cnt++; /* bump count */
} else {
/* got to empty FIFO */
(void) inportb(base+DATA);
write_scc(hp->base+CTL,R0,ERR_RES); /* reset err latch */
hp->rstate = ACTIVE;
}
}
/* char has been stored
* read special condition bits
*/
rse = read_scc(hp->base+CTL,R1); /* get status byte from R1 */
/* The End of Frame bit is ALWAYS associated with a character,
* usually, it is the last CRC char. Only when EOF is true can
* we look at the CRC byte to see if we have a valid frame
*/
if(rse & END_FR) {
hp->rxframes++;
/* END OF FRAME -- Make sure Rx was active */
if(hp->rcvbuf->cnt > 0) { /* any data to store */
/* looks like a frame was received
* now is the only time we can check for CRC error
*/
if((rse & CRC_ERR) || (hp->rstate > ACTIVE) || (hp->rcvbuf->cnt < 10)) {
/* error occurred; toss frame */
if(rse & CRC_ERR)
hp->crcerr++; /* count CRC errs */
if(hp->rstate == RXERROR)
hp->rovers++;
/* don't throw away buffer -
* merely reset the pointers
*/
hp->rcp = hp->rcvbuf->data;
hp->rcvbuf->cnt = 0;
} else {
/* Here we have a valid frame */
hp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */
enqueue(&hp->rcvq,hp->rcvbuf); /* queue it in */
hp->enqueued++;
/* packet queued - get buffer for next frame */
hp->rcvbuf = alloc_mbuf(hp->bufsiz);
hp->rcp = hp->rcvbuf->data;
hp->rcvbuf->cnt = 0;
if(hp->rcvbuf == NULLBUF) {
/* No memory, abort receiver */
restore(i_state);
printf("DISASTER! Out of Memory for Receive!\n");
fflush(stdout);
write_scc(CTL+base,R3,Rx8);
return;
}
} /* end good frame queued */
} /* end check for active receive upon EOF */
hp->rstate = ACTIVE; /* and clear error status */
} /* end EOF check */
restore(i_state);
}
/* egchan transmit interrupt service routine
*
* The state variable tstate, along with some static pointers,
* represents the state of the transmit "process".
*/
static void
egtxint(hp)
register struct egchan *hp;
{
register int16 base;
char i_state,c;
i_state = disable();
if(hp->tstate != DEFER && hp->tstate) hp->txints++;
base = hp->base;
switch(hp->tstate) {
case FLAGOUT:
/* Here after CRC sent and Tx interrupt fires.
* To give the SCC a chance to get the FLAG
* out, we delay 100 Ms
*/
hp->tstate = IDLE; /* fall thru to IDLE */
waitmsec(10); /* 100 msec wait for flag Tx */
/* Note, it may be possible to stuff out a
* meaningless character, wait for the interrupt
* then go to idle. A delay is absolutely necessary
* here else the packet gets truncated prematurely
* when no other packet is waiting to be sent.
* IDLE state indicates either we are starting a brand new packet
* as a result of its being queued for transmission (egtxint called
* from eg_raw), or after a frame has been transmitted (as a
* result of a Tx buffer empty interrupt after the CRC/FLAG
*/
case IDLE:
/* Transmitter idle. Find a frame for t